package io.github.talelin.latticy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import io.github.talelin.autoconfigure.exception.ForbiddenException;
import io.github.talelin.autoconfigure.exception.NotFoundException;
import io.github.talelin.latticy.dto.warearea.UpdateWareareaDTO;
import io.github.talelin.latticy.dto.warearea.WareareaDTO;
import io.github.talelin.latticy.model.LocationDO;
import io.github.talelin.latticy.model.WareareaDO;
import io.github.talelin.latticy.mapper.WareareaMapper;
import io.github.talelin.latticy.service.LocationService;
import io.github.talelin.latticy.service.WareareaService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.github.talelin.latticy.service.WarehouseService;
import io.github.talelin.latticy.vo.WareareaVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 仓库分区 服务实现类
 * </p>
 *
 * @author generator@wulongbo
 * @since 2022-06-07
 */
@Service
public class WareareaServiceImpl extends ServiceImpl<WareareaMapper, WareareaDO> implements WareareaService {

    @Autowired
    private LocationService locationService;

    @Autowired
    private WarehouseService warehouseService;

    @Transactional
    @Override
    public WareareaDO createWareArea(WareareaDTO dto) {
        this.checkCreateWareArea(dto);
        WareareaDO wareArea = new WareareaDO(dto);
        this.baseMapper.insert(wareArea);
        return wareArea;
    }

    @Transactional
    @Override
    public boolean updateWareArea(int id, UpdateWareareaDTO dto) {
        if (StringUtils.isNotEmpty(dto.getName())) {
            int rows = this.baseMapper.selectCountByIdAndName(id, dto.getName());
            if (rows > 0) {
                throw new ForbiddenException(18400);
            }
        }
        if (dto.getAreaOrder() != null) {
            boolean exist = this.checkWareAreaOrderExistById(id, dto.getAreaOrder());
            if (exist) {
                WareareaDO wareareaDO = this.baseMapper.selectById(id);
                this.updateAreaOrder(wareareaDO.getCode(), dto.getAreaOrder());
            }
        }
        WareareaDO warearea = new WareareaDO(id, dto);
        return this.baseMapper.updateById(warearea) > 0;
    }

    @Override
    public boolean deleteWareArea(int id) {
        boolean exist = locationService.checkLocationExistByLocationId(id);
        if (exist) {
            throw new ForbiddenException(18402);
        }
        return this.baseMapper.deleteById(id) > 0;
    }

    @Override
    public WareareaDO getWareAreaById(int id) {
        WareareaDO wareareaDO = this.baseMapper.selectById(id);
        Optional.ofNullable(wareareaDO).orElseThrow(() -> new NotFoundException(18401));
        return wareareaDO;
    }

    @Override
    public List<WareareaVO> getWareAreaList(String code) {
        QueryWrapper<WareareaDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(WareareaDO::getStatus, 1).eq(WareareaDO::getCode, code);
        List<WareareaDO> wareAreaList = this.baseMapper.selectList(wrapper);
        return wareAreaList.parallelStream().map(wareAreaDO -> {
            QueryWrapper<LocationDO> queryWrapper = new QueryWrapper<>();
            queryWrapper.lambda().eq(LocationDO::getAreaId, wareAreaDO.getId());
            int count = locationService.count(queryWrapper);
            return new WareareaVO(wareAreaDO, count);
        }).collect(Collectors.toList());
    }

    @Override
    public String getCodeByAreaId(int areaId) {
        return this.baseMapper.getCodeByAreaId(areaId);
    }

    @Override
    public boolean checkWareAreaExistById(int id) {
        QueryWrapper<WareareaDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(WareareaDO::getId, id);
        return this.count(wrapper) > 0;
    }

    @Override
    public boolean checkWareAreaOrderExistById(int id, int areaOrder) {
        WareareaDO wareareaDO = this.baseMapper.selectById(id);
        Optional.ofNullable(wareareaDO).orElseThrow(() -> new NotFoundException(18401));
        int rows = this.baseMapper.selectCountByCodeAndOrder(wareareaDO.getCode(), areaOrder);
        return rows > 0;
    }

    @Override
    public boolean checkWareAreaOrderExistByCode(String code, int areaOrder) {
        int rows = this.baseMapper.selectCountByCodeAndOrder(code, areaOrder);
        return rows > 0;
    }

    @Override
    public boolean checkWareAreaExistByName(String code, String name) {
        int rows = this.baseMapper.selectCountByName(code, name);
        return rows > 0;
    }

    private void checkCreateWareArea(WareareaDTO dto) {
        boolean exist = warehouseService.checkWarehouseExistByCode(dto.getCode());
        if (!exist) {
            throw new ForbiddenException(18301);
        }
        exist = this.checkWareAreaExistByName(dto.getCode(), dto.getName());
        if (exist) {
            throw new ForbiddenException(18400);
        }
        exist = this.checkWareAreaOrderExistByCode(dto.getCode(), dto.getAreaOrder());
        if (exist) {
            this.updateAreaOrder(dto.getCode(), dto.getAreaOrder());
        }
    }

    private void updateAreaOrder(String code, int areaOrder) {
        UpdateWrapper<WareareaDO> wrapper = new UpdateWrapper<>();
        wrapper.eq("code", code).ge("area_order", areaOrder);
        wrapper.setSql("`area_order` = `area_order` + 1");
        this.update(wrapper);
    }
}
